/**
 * @description
 * 这是一个地形蓝图工具库 (TerrainSchematic) 的功能展示脚本。
 * 它会在游戏世界中生成一个两层楼的宏大结构，每一层都摆满了各种由代码生成的形状和物体。
 * 这个脚本的目的是为了全面、直观地展示 TerrainSchematic 库的各项功能及其组合使用的强大能力。
 *
 * 主要结构：
 * - 定义全局常量 (方块ID)
 * - 辅助函数 (清理区域、放置形状和标签)
 * - 主函数 `showcase()`:
 *   - 定义布局参数 (间距、大小)
 *   - 为每个展示品定义独立的创建函数 (例如 `createCuboid()`, `createIsland()`)
 *   - 将所有展示品分配到一层和二层的网格中
 *   - 调用 `generateLayer()` 循环创建并放置所有物体
 */
import TerrainSchematic from "./terrain/schematic";

// =================================================================
// 全局常量定义
// =================================================================

/** 用于构成主要形状的方块ID (例如：木板) */
const SHAPE_VOXEL_ID: voxelId = 321; // 木板
/** 用于创建3D文字标签的方块ID (例如：钻石块，使其醒目) */
const TEXT_VOXEL_ID: voxelId = 581; // 钻石块，让文字更醒目
/** 用于创建辅助或对比形状的方块ID (例如：圆石) */
const ALT_VOXEL_ID: voxelId = 231; // 圆石

// =================================================================
// 辅助函数
// =================================================================

/**
 * 清理指定的游戏世界区域，将其填充为空气。
 * @param lo 区域的最低点坐标 (low corner)
 * @param hi 区域的最高点坐标 (high corner)
 */
function clearArea(lo: GameVector3, hi: GameVector3) {
  const clearSchematic = new TerrainSchematic(new GameBounds3(lo, hi));
  clearSchematic.replaceAllBlocks(0, 0, []); // 0是空气的ID
  clearSchematic.import(lo);
  console.log(`区域 ${lo.toString()} - ${hi.toString()} 已清空。`);
}

/**
 * 将一个已创建的蓝图实例导入到世界中，并在其正上方居中放置一个3D文字标签。
 * @param shapeName 要在标签上显示的文字
 * @param schematic 要放置的形状的 `TerrainSchematic` 实例
 * @param basePosition 形状的导入基准点，通常是其几何中心对齐到区域中心后的坐标
 * @param areaCenter 形状所在展示区域的中心点，用于旧的居中逻辑（当前已弃用）
 */
function createAndPlaceShape(
  shapeName: string,
  schematic: TerrainSchematic,
  basePosition: GameVector3,
  areaCenter: GameVector3
) {
  // 1. 将形状导入世界
  schematic.import(basePosition);

  // 2. 创建文字标签
  const text = TerrainSchematic.fromText(
    shapeName.toUpperCase(),
    TEXT_VOXEL_ID,
    0
  );

  // 3. 计算精确的居中位置
  const shapeDimensions = schematic.getDimensions();
  const textDimensions = text.getDimensions();

  // 形状的实际几何中心点 (X 和 Z 坐标)
  const shapeCenterX = basePosition.x + Math.floor(shapeDimensions.x / 2);
  const shapeCenterZ = basePosition.z + Math.floor(shapeDimensions.z / 2);

  // 计算文字的位置，使其几何中心与形状的几何中心对齐，并置于形状正上方
  const textPosition = new GameVector3(
    shapeCenterX - Math.floor(textDimensions.x / 2),
    basePosition.y + shapeDimensions.y + 2, // Y轴上留出2格空隙
    shapeCenterZ - Math.floor(textDimensions.z / 2)
  );

  // 4. 将文字导入世界
  text.import(textPosition);

  console.log(`成功创建并放置了 ${shapeName}。`);
}

// =================================================================
// 主函数 - 总指挥
// =================================================================

/**
 * 主函数，负责生成整个地形展示结构。
 */
export function showcase() {
  console.log("开始地形蓝图工具库功能展示...");

  // --- 布局参数 ---
  const globalOffset = new GameVector3(16, 0, 16); // 全局偏移，避免从(0,0,0)开始建，方便观察
  const baseHeight = 3; // 地面层的高度 (原为10)
  const areaSize = 28; // 每个展示区域的大小
  const shapeHeight = 10; // 大部分形状的默认高度
  const spacing = 45; // 展示区域之间的间距

  // ===============================================================
  // 形状创建工厂函数 (Factories)
  // ===============================================================
  // 将每个形状的创建逻辑封装在独立的函数中，使主列表更清晰。

  // --- Floor 1 Factories ---
  const f1 = {
    cuboid: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(areaSize - 8, shapeHeight, areaSize - 8),
        SHAPE_VOXEL_ID,
        0,
        { isHollow: true }
      ),
    sphere: () =>
      TerrainSchematic.fromSphere((areaSize - 8) / 2, SHAPE_VOXEL_ID, 0, {
        isHollow: true,
      }),
    pyramid: () =>
      TerrainSchematic.fromPyramid(
        areaSize - 8,
        shapeHeight,
        SHAPE_VOXEL_ID,
        0,
        { isHollow: true }
      ),
    cone: () =>
      TerrainSchematic.fromCone(
        (areaSize - 8) / 2,
        shapeHeight,
        SHAPE_VOXEL_ID,
        0,
        { isHollow: true }
      ),
    pipe: () =>
      TerrainSchematic.fromPipe(
        shapeHeight,
        (areaSize - 8) / 2,
        2,
        SHAPE_VOXEL_ID,
        0
      ),
    torus: () =>
      TerrainSchematic.fromTorus((areaSize - 10) / 2, 2, SHAPE_VOXEL_ID, 0),
    prism: () =>
      TerrainSchematic.fromPrism(
        6,
        (areaSize - 8) / 2,
        shapeHeight,
        SHAPE_VOXEL_ID,
        0,
        { isHollow: true }
      ),
    capsule: () =>
      TerrainSchematic.fromCapsule(shapeHeight - 8, 4, SHAPE_VOXEL_ID, 0),
    thicken: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(12, 12, 1),
        SHAPE_VOXEL_ID
      ).thicken(2, ALT_VOXEL_ID),
    shell: () => TerrainSchematic.fromSphere(8, SHAPE_VOXEL_ID).shell(2),
    subtract: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(areaSize - 8, areaSize - 8, areaSize - 8),
        SHAPE_VOXEL_ID
      ).subtract(
        TerrainSchematic.fromSphere((areaSize - 8) / 2, SHAPE_VOXEL_ID)
      ),
    wire: () =>
      TerrainSchematic.fromSphere((areaSize - 8) / 2, SHAPE_VOXEL_ID, 0, {
        isHollow: true,
      }).wireFrame(1),
    sect: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(12, 12, 12),
        SHAPE_VOXEL_ID
      ).intersect(
        TerrainSchematic.fromSphere(8, ALT_VOXEL_ID),
        new GameVector3(-2, -2, -2)
      ),
    flip: () =>
      TerrainSchematic.fromSphere(6, SHAPE_VOXEL_ID)
        .add(
          TerrainSchematic.fromCuboid(new GameVector3(10, 3, 3), ALT_VOXEL_ID),
          new GameVector3(5, 0, 0)
        )
        .flip("x"),
    mirror: () =>
      TerrainSchematic.fromSphere(6, SHAPE_VOXEL_ID)
        .add(
          TerrainSchematic.fromCuboid(new GameVector3(10, 3, 3), ALT_VOXEL_ID),
          new GameVector3(5, 0, 0)
        )
        .mirror("x"),
    grad: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(10, shapeHeight + 8, 10),
        SHAPE_VOXEL_ID
      ).gradientReplace(
        "y",
        [
          1 as voxelId,
          4 as voxelId,
          5 as voxelId,
          211 as voxelId,
          22 as voxelId,
        ],
        true
      ),
    erode: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(areaSize - 4, shapeHeight, areaSize - 4),
        ALT_VOXEL_ID
      ).erode({ iterations: 5 }),
    clone: () =>
      TerrainSchematic.fromSphere(
        (areaSize - 8) / 2,
        SHAPE_VOXEL_ID
      ).cloneToSurface(TerrainSchematic.fromSphere(2, TEXT_VOXEL_ID), {
        density: 0.2,
        alignToSurface: true,
      }),
    decay: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(areaSize - 4, shapeHeight, 4),
        ALT_VOXEL_ID
      ).decay(0.35, true),
    smooth: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(areaSize - 8, shapeHeight, areaSize - 8),
        SHAPE_VOXEL_ID
      )
        .decay(0.6, false)
        .smooth({ iterations: 5 }),
    path: () =>
      TerrainSchematic.fromPathWall(
        [
          new GameVector3(0, 10, 0),
          new GameVector3(5, 15, 10),
          new GameVector3(15, 20, 15),
          new GameVector3(40, 30, 25),
          new GameVector3(15, 20, 30),
        ],
        shapeHeight,
        2,
        SHAPE_VOXEL_ID,
        0,
        { useBezier: true }
      ),
    text: () => TerrainSchematic.fromText("HELLO", TEXT_VOXEL_ID, 0),
    stack: () =>
      TerrainSchematic.fromTorus(8, 2, SHAPE_VOXEL_ID, 0)
        .add(
          TerrainSchematic.fromTorus(6, 2, SHAPE_VOXEL_ID, 0),
          new GameVector3(0, 4, 0)
        )
        .add(
          TerrainSchematic.fromTorus(4, 1, SHAPE_VOXEL_ID, 0),
          new GameVector3(0, 8, 0)
        )
        .stack(new GameVector3(2, 3, 3)),
    pivot: () =>
      TerrainSchematic.fromCuboid(new GameVector3(12, 2, 2), SHAPE_VOXEL_ID)
        .setPivot(new GameVector3(6, 0, 0))
        .rotate("z", 45),
    pattern: () =>
      TerrainSchematic.fromCuboid(
        new GameVector3(areaSize - 4, shapeHeight, 2),
        SHAPE_VOXEL_ID
      ).replaceByPattern(
        new Map([
          [
            SHAPE_VOXEL_ID,
            [
              { id: ALT_VOXEL_ID, weight: 1 },
              { id: TEXT_VOXEL_ID, weight: 1 },
            ],
          ],
        ])
      ),
  };

  // --- Floor 2 Factories ---
  const f2 = {
    island: () => {
      const islandBase = TerrainSchematic.fromSphere(14, ALT_VOXEL_ID)
        .erode({ iterations: 2, depositSpeed: 0.5 })
        .shell(4);
      const grass = TerrainSchematic.fromCylinder(10, 1, 2 as voxelId);
      const trees = TerrainSchematic.fromSphere(2, 34 as voxelId);
      islandBase.add(grass, new GameVector3(0, 5, 0));
      islandBase.cloneToSurface(trees, {
        density: 0.1,
        allowedSurfaceNormals: [new GameVector3(0, 1, 0)],
      });
      return islandBase;
    },
    qMirror: () => {
      const shape = TerrainSchematic.fromCone(6, 12, SHAPE_VOXEL_ID, 0, {
        isHollow: true,
      });
      shape.add(
        TerrainSchematic.fromCuboid(new GameVector3(12, 3, 3), ALT_VOXEL_ID),
        new GameVector3(5, 5, 0)
      );
      shape.mirror("x");
      shape.mirror("z");
      return shape;
    },
    castle: () => {
      const wallSection = TerrainSchematic.fromCuboid(
        new GameVector3(4, 6, 2),
        SHAPE_VOXEL_ID
      );
      const battlement = TerrainSchematic.fromCuboid(
        new GameVector3(1, 1, 1),
        0
      );
      wallSection
        .subtract(battlement, new GameVector3(1, 6, 0))
        .subtract(battlement, new GameVector3(3, 6, 0));
      const path = [
        new GameVector3(0, 0, 0),
        new GameVector3(15, 0, 5),
        new GameVector3(15, 0, 15),
        new GameVector3(5, 0, 15),
        new GameVector3(0, 0, 0),
      ];
      return TerrainSchematic.fromPath(wallSection, path, {
        step: 3,
        alignToPath: true,
      });
    },
    menger: () => {
      let sponge = TerrainSchematic.fromCuboid(
        new GameVector3(27, 27, 27),
        SHAPE_VOXEL_ID
      );
      for (let scale of [9, 3, 1]) {
        for (let x = 0; x < 3; x++)
          for (let y = 0; y < 3; y++)
            for (let z = 0; z < 3; z++) {
              if (
                (x === 1 && y === 1) ||
                (y === 1 && z === 1) ||
                (x === 1 && z === 1)
              ) {
                sponge.subtract(
                  TerrainSchematic.fromCuboid(
                    new GameVector3(scale, scale, scale),
                    0
                  ),
                  new GameVector3(x * scale, y * scale, z * scale)
                );
              }
            }
      }
      return sponge;
    },
    spiral: () => {
      const path = [];
      for (let i = 0; i < 720; i += 15)
        path.push(
          new GameVector3(
            Math.cos((i * Math.PI) / 180) * 8,
            i / 20,
            Math.sin((i * Math.PI) / 180) * 8
          )
        );
      return TerrainSchematic.fromPath(
        TerrainSchematic.fromCuboid(new GameVector3(4, 1, 6), SHAPE_VOXEL_ID),
        path,
        { alignToPath: true }
      );
    },
    knot: () => {
      const torusX = TerrainSchematic.fromTorus(8, 3, SHAPE_VOXEL_ID);
      return torusX
        .clone()
        .add(torusX.clone().rotate("y", 90))
        .add(torusX.clone().rotate("z", 90));
    },
    replaceAll: () =>
      TerrainSchematic.fromTorus(10, 4, SHAPE_VOXEL_ID)
        .add(TerrainSchematic.fromSphere(6, ALT_VOXEL_ID))
        .replaceAllBlocks(21 as voxelId, 0),
    replace: () => {
      const cube = TerrainSchematic.fromCuboid(new GameVector3(11, 11, 11), 0);
      for (let x = 0; x < 11; x += 2)
        for (let y = 0; y < 11; y += 2)
          for (let z = 0; z < 11; z += 2)
            cube.add(
              TerrainSchematic.fromCuboid(
                new GameVector3(1, 1, 1),
                SHAPE_VOXEL_ID
              ),
              new GameVector3(x, y, z)
            );
      return cube.replaceBlocks(
        new Map([[SHAPE_VOXEL_ID, { id: 211 as voxelId }]])
      );
    },
    pathMirror: () =>
      TerrainSchematic.fromPathWall(
        [
          new GameVector3(0, 0, 0),
          new GameVector3(10, 5, 5),
          new GameVector3(0, 10, 10),
        ],
        15,
        2,
        SHAPE_VOXEL_ID,
        0,
        { useBezier: true }
      ).mirror("x"),
    dnaHelix: () => {
      const finalSchematic = new TerrainSchematic(
        new GameBounds3(new GameVector3(0, 0, 0), new GameVector3(0, 0, 0))
      );
      const sphereStamp = TerrainSchematic.fromSphere(1, TEXT_VOXEL_ID);
      const barStamp = TerrainSchematic.fromCuboid(
        new GameVector3(1, 1, 10),
        ALT_VOXEL_ID
      );

      const path1 = [],
        path2 = [];
      for (let i = 0; i < 360; i += 15) {
        const angle = (i * Math.PI) / 180;
        const y = i / 18;
        path1.push(
          new GameVector3(Math.cos(angle) * 5, y, Math.sin(angle) * 5)
        );
        path2.push(
          new GameVector3(
            Math.cos(angle + Math.PI) * 5,
            y,
            Math.sin(angle + Math.PI) * 5
          )
        );
      }

      finalSchematic.add(
        TerrainSchematic.fromPath(sphereStamp, path1, {
          step: 1,
          useBezier: true,
          bezierSampleCount: 50,
        })
      );
      finalSchematic.add(
        TerrainSchematic.fromPath(sphereStamp, path2, {
          step: 1,
          useBezier: true,
          bezierSampleCount: 50,
        })
      );

      for (let i = 0; i < path1.length; i++) {
        finalSchematic.add(
          barStamp.clone(),
          path1[i].add(new GameVector3(0, 0, -5))
        );
      }

      return finalSchematic;
    },
    gear: () => {
      const gear = TerrainSchematic.fromCylinder(10, 4, SHAPE_VOXEL_ID, 0, {
        isHollow: true,
        thickness: 3,
      });
      const toothCount = 12;
      for (let i = 0; i < toothCount; i++) {
        const angle = (i / toothCount) * 360;
        const rad = (angle * Math.PI) / 180;
        const tooth = TerrainSchematic.fromCuboid(
          new GameVector3(4, 3, 2),
          ALT_VOXEL_ID
        ).rotate("y", angle);
        const pos = new GameVector3(Math.cos(rad) * 10, 0, Math.sin(rad) * 10);
        gear.add(tooth, pos);
      }
      return gear;
    },
  };

  // ===============================================================
  // 网格布局定义
  // ===============================================================

  /** 定义第一层的所有展品及其在网格中的位置 */
  const shapesToShow_floor1 = [
    // --- Row 0: 基础图形 ---
    { name: "cuboid", gridPos: new GameVector3(0, 0, 0), factory: f1.cuboid },
    { name: "sphere", gridPos: new GameVector3(1, 0, 0), factory: f1.sphere },
    { name: "pyramid", gridPos: new GameVector3(2, 0, 0), factory: f1.pyramid },
    { name: "cone", gridPos: new GameVector3(3, 0, 0), factory: f1.cone },
    { name: "pipe", gridPos: new GameVector3(4, 0, 0), factory: f1.pipe },
    // --- Row 1: 更多图形 ---
    { name: "torus", gridPos: new GameVector3(0, 1, 0), factory: f1.torus },
    { name: "prism", gridPos: new GameVector3(1, 1, 0), factory: f1.prism },
    { name: "capsule", gridPos: new GameVector3(2, 1, 0), factory: f1.capsule },
    { name: "thicken", gridPos: new GameVector3(3, 1, 0), factory: f1.thicken },
    { name: "shell", gridPos: new GameVector3(4, 1, 0), factory: f1.shell },
    // --- Row 2: 布尔运算与变换 ---
    {
      name: "subtract",
      gridPos: new GameVector3(0, 2, 0),
      factory: f1.subtract,
    },
    { name: "wire", gridPos: new GameVector3(1, 2, 0), factory: f1.wire },
    { name: "sect", gridPos: new GameVector3(2, 2, 0), factory: f1.sect },
    { name: "flip", gridPos: new GameVector3(3, 2, 0), factory: f1.flip },
    { name: "mirror", gridPos: new GameVector3(4, 2, 0), factory: f1.mirror },
    // --- Row 3: 修改与效果 ---
    { name: "grad", gridPos: new GameVector3(0, 3, 0), factory: f1.grad },
    { name: "erode", gridPos: new GameVector3(1, 3, 0), factory: f1.erode },
    { name: "clone", gridPos: new GameVector3(2, 3, 0), factory: f1.clone },
    { name: "decay", gridPos: new GameVector3(3, 3, 0), factory: f1.decay },
    { name: "smooth", gridPos: new GameVector3(4, 3, 0), factory: f1.smooth },
    // --- Row 4: 路径、文字与高级功能 ---
    { name: "path", gridPos: new GameVector3(0, 4, 0), factory: f1.path },
    { name: "text", gridPos: new GameVector3(1, 4, 0), factory: f1.text },
    { name: "stack", gridPos: new GameVector3(2, 4, 0), factory: f1.stack },
    { name: "pivot", gridPos: new GameVector3(3, 4, 0), factory: f1.pivot },
    { name: "pattern", gridPos: new GameVector3(4, 4, 0), factory: f1.pattern },
  ];

  /** 定义第二层的所有展品及其在网格中的位置 */
  const shapesToShow_floor2 = [
    { name: "island", gridPos: new GameVector3(0, 0, 0), factory: f2.island },
    {
      name: "q-mirror",
      gridPos: new GameVector3(1, 0, 0),
      factory: f2.qMirror,
    },
    { name: "castle", gridPos: new GameVector3(0, 1, 0), factory: f2.castle },
    { name: "menger", gridPos: new GameVector3(2, 0, 0), factory: f2.menger },
    { name: "spiral", gridPos: new GameVector3(1, 1, 0), factory: f2.spiral },
    { name: "knot", gridPos: new GameVector3(2, 1, 0), factory: f2.knot },
    {
      name: "replaceAll",
      gridPos: new GameVector3(0, 2, 0),
      factory: f2.replaceAll,
    },
    { name: "replace", gridPos: new GameVector3(1, 2, 0), factory: f2.replace },
    {
      name: "path-mirror",
      gridPos: new GameVector3(2, 2, 0),
      factory: f2.pathMirror,
    },
    {
      name: "dna-helix",
      gridPos: new GameVector3(3, 0, 0),
      factory: f2.dnaHelix,
    },
    { name: "gear", gridPos: new GameVector3(3, 1, 0), factory: f2.gear },
  ];

  // ===============================================================
  // 生成执行
  // ===============================================================

  /**
   * 通用的层生成器函数。
   * @param shapes 要在这一层生成的形状对象数组
   * @param layerBaseHeight 这一层的基准Y坐标
   */
  function generateLayer(shapes: any[], layerBaseHeight: number) {
    // 1. 清理所有将要使用的区域
    for (const shapeInfo of shapes) {
      const lo = new GameVector3(
        shapeInfo.gridPos.x * spacing + globalOffset.x,
        layerBaseHeight - 1,
        shapeInfo.gridPos.y * spacing + globalOffset.z
      );
      const hi = new GameVector3(
        lo.x + areaSize,
        layerBaseHeight + shapeHeight + 15,
        lo.z + areaSize
      );
      clearArea(lo, hi);
    }

    // 2. 循环创建和放置所有形状
    for (const shapeInfo of shapes) {
      const schematic = shapeInfo.factory();
      const dimensions = schematic.getDimensions();

      const areaLo = new GameVector3(
        shapeInfo.gridPos.x * spacing + globalOffset.x,
        layerBaseHeight,
        shapeInfo.gridPos.y * spacing + globalOffset.z
      );

      const basePosition = new GameVector3(
        areaLo.x + Math.floor((areaSize - dimensions.x) / 2),
        layerBaseHeight,
        areaLo.z + Math.floor((areaSize - dimensions.z) / 2)
      );

      createAndPlaceShape(shapeInfo.name, schematic, basePosition, areaLo);
    }
  }

  // --- 执行生成 ---
  console.log("--- Generating Floor 1 ---");
  generateLayer(shapesToShow_floor1, baseHeight);

  const floor2BaseHeight = 33;
  console.log("--- Generating Floor 2 ---");
  generateLayer(shapesToShow_floor2, floor2BaseHeight);

  console.log("所有形状已生成完毕！");
}

// 在服务器启动时或通过某个指令调用此函数，实现自动化运行
showcase();
